#include "liolib/lioflat.h"

#define DEF_START 0.0
#define DEF_END 1.5
#define DEF_SAMPLES 16
#define DEF_POINTS 10

class LSM:public FlatApplication
{
	int coefCount;
	Vector<double> coefs,tmp;
	int N,dN;
	double realValue,t,error,start,end,lsmValue,dev;
	Vector<double> vy,vx;
	static CSTR formula;
	Font fontSmall;
	FlatText tbCoefs,tbNoise,tbStart,tbEnd,tbSamples,tbPoints,tbFile;
	FlatButton bPlus,bMin,bAuto,bNoise,bInter,bSamples,bCapture,bImport,bLog;
	FlatGraph fg;

	BOOL create();
	void draw2D();
	BOOL listen(WORD id, WORD code, const void *info);

	BOOL setCoefs(int n);
	BOOL tryCoefs(int n);
	BOOL setNoise(double noise);
	BOOL setSamples(int samples, int points);
	void setInterval(double start, double end);
	void interpolate();
	double getError();
	void reshape();
	BOOL import(CSTR filename, BOOL isLog);
public:
	static double func(double x);
} lsm;

CSTR LSM::formula=_T("(x-0.1)/(x+0.1)");

void LSM::reshape()
{
	FlatApplication::reshape();
	fg.size.set(float(window.width-270),float(window.height-20));
	container.build();
}

double LSM::func(double x)
{
	return (x-0.1)/(x+0.1);
}
BOOL LSM::import(CSTR filename, BOOL isLog)
{
	File file;
	BOOL result=FALSE;
	if(file.open(filename))
	{
		int size;
		file.read(&size,1);
		if(size>0&&vx.setSize(size)&&vy.setSize(size))
		{
			file.read(vx.elems,size);
			file.read(vy.elems,size);
			double min,max;
			min=max=vx.elems[0];
			for(int i=1;i<size;i++)
			{
				if(vx.elems[i]>max)
					max=vx.elems[i];
				else if(vx.elems[i]<min)
					min=vx.elems[i];
				if(isLog)
					vy.elems[i]=log(vy.elems[i]);
			}
			setInterval(min,max);
			setSamples(size,dN);
			setCoefs(coefCount);
			result=TRUE;
		}
		file.close();
	}
	return result;
}
BOOL LSM::listen(WORD id, WORD code, const void *info)
{
	if(id==tbNoise.id||id==bNoise.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setNoise(format.toDouble(tbNoise.text));
			setCoefs(coefCount);
		}
	}
	else if(id==tbCoefs.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setCoefs(format.toInt(tbCoefs.text));
		}
	}
	else if(id==bImport.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			import(tbFile.getText(),bLog.checked);
		}
	}
	else if(id==bPlus.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setCoefs(coefCount+1);
		}
	}
	else if(id==bMin.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setCoefs(coefCount-1);
		}
	}
	else if(id==bAuto.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			interpolate();
		}
	}
	else if(id==tbStart.id||id==tbEnd.id||id==bInter.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setInterval(	format.toDouble(tbStart.text),
							format.toDouble(tbEnd.text));
			setNoise(dev);
			setCoefs(coefCount);
		}
	}
	else if(id==tbSamples.id||id==tbPoints.id||id==bSamples.id)
	{
		if(code==FLAT_ACTION_DEFAULT)
		{
			setSamples(	format.toInt(tbSamples.text),
						format.toInt(tbPoints.text));
			setNoise(dev);
			setCoefs(coefCount);
		}
	}
	else if(id==bCapture.id)
	{
		Image image;
		String str;
		InitInfo info;
		info.format.exposant=TRUE;
		info.format.precision=3;
		info.add(_T("Formula"),formula);
		info.addInt(_T("Samples"),N);
		info.add(_T("Noise"),tbNoise.text);
		info.add(_T("Coefs"),tbCoefs.text);
		info.addDouble(_T("Error"),error);
		str=_T("a");
		for(int i=0;i<coefCount;i++)
			info.addDouble(str+format.toString(i+1),coefs.elems[i]);
		screenshot(&image,fg.getRect());
		str=_T("capture_");
		str+=tbNoise.text;
		info.write(str+_T(".txt"));
		writeJPEG(str+_T(".jpg"),&image,85);
	}
	else
	{
		return FALSE;
	}
	return TRUE;
}
void LSM::interpolate()
{
	int n,bestN;
	double minError;
	n=bestN=2;
	tryCoefs(n);
	minError=error;
	while(tryCoefs(++n))
	{
		if(minError>error)
		{
			minError=error;
			bestN=n;
		}
	}
	setCoefs(bestN);
}
BOOL LSM::setSamples(int samples, int points)
{
	if(	samples<2||points<1||
		!vx.setSize(samples)||!vy.setSize(samples))
		return FALSE;
	N=samples;
	dN=points;
	tbSamples.setText(format.toString(N));
	tbPoints.setText(format.toString(dN));

	fg.graph.getChannelByName(_T("time"))->setSource(&t,N*dN);
	fg.graph.getChannelByName(_T("real"))->setSource(&realValue,N*dN);
	fg.graph.getChannelByName(_T("LSM"))->setSource(&lsmValue,N*dN);
	fg.graph.getChannelByName(_T("real(LSM)"))->setSource(&realValue,N*dN);
	fg.graph.getChannelByName(_T("x_anchors"))->setSource(vx.elems,-vy.length);
	fg.graph.getChannelByName(_T("y_anchors"))->setSource(vy.elems,-vy.length);

	return TRUE;
}
void LSM::setInterval(double start, double end)
{
	this->start=start;
	this->end=end;
	tbStart.setText(format.toString(start));
	tbEnd.setText(format.toString(end));
}
double LSM::getError()
{
	double error=0;
	for(int i=0;i<N;i++)
		error+=pow2(funcValue(coefs,coefCount,vx.elems[i])-vy.elems[i]);
	return sqrt(error/N);
}
BOOL LSM::tryCoefs(int n)
{
	if(	!tmp.setSize(n)||
		!getLSMPolyCoef(vy.elems,vx.elems,N,tmp.elems,n)||
		!coefs.copy(tmp.elems,n))
		return FALSE;
	coefCount=n;
	error=getError();
	return TRUE;
}

BOOL LSM::setNoise(double noise)
{
	if(noise<0)
		return FALSE;
	dev=noise;
	tbNoise.setText(format.toString(dev));
	incData<double>(start,end,vx.length,vx.elems);
	incData<double>(start,end,vy.length,vy.elems,func);
	addNoise(vy.elems,vy.length,dev);
	return TRUE;
}
BOOL LSM::setCoefs(int n)
{
	if(!tryCoefs(n))
		return FALSE;
	tbCoefs.setText(format.toString(coefCount));
	fg.graph.clear();
	int i,count=N*dN;
	double step=(end-start)/(count-1);
	for(i=0,t=start;i<count;i++,t+=step)
	{
		realValue=func(t);
		lsmValue=funcValue(coefs,coefCount,t);
		fg.graph.addValues();
	}
	fg.graph.update();
	return TRUE;
}
BOOL LSM::create ()	
{
	Channel *tmp;

	FlatApplication::create();
	name=_T("Least Squares Method");

	display.width=800;
	display.height=600;

	format.precision=3;

	fontSmall.setFace(_T("Courier New"));
	fontSmall.height=-12;
	add(&fontSmall);

	fg.bgColor=INT_GRAY;
	fg.graph.clear();
	fg.graph.smooth=TRUE;
	fg.graph.grid.set(3,5);
	fg.graph.margin=10;
	fg.graph.marginChannel.set(15,50);
	fg.graph.font=&fontSmall;
	fg.graph.formatX.precision=3;
	fg.graph.formatY.precision=3;
	fg.location.set(250,10);
	fg.margin.set(10,10);
	fg.font=&font;
	fg.fontColor=INT_BLACK;
	fg.graph.addChannel( _T("time"));
	tmp=fg.graph.addChannel( _T("real"),_T("time"),INT_MAGENTA);
	tmp->setStipple(0xDDDD,2);
	tmp->symbol=_T("1");
	tmp=fg.graph.addChannel( _T("LSM"),_T("time"),INT_BLUE);
	tmp->symbol=_T("2");
	fg.graph.addChannel( _T("x_anchors"));
	fg.graph.addChannel( _T("y_anchors"),_T("x_anchors"),INT_BLACK,0,NULL,-10);
	fg.graph.addChannel( _T("real(LSM)"),_T("LSM"),INT_RED,0,NULL,2,TRUE,TRUE);

	tbCoefs.borderLine=1;
	tbCoefs.borderColor=INT_WGRAY;
	tbCoefs.activeLine=2;
	tbCoefs.font=&font;
	tbCoefs.fontColor=INT_BLACK;
	tbCoefs.margin.set(2,1);
	tbCoefs.setBounds(10,80,40,20);

	bPlus.text=_T("+");
	bPlus.borderLine=1;
	bPlus.borderColor=INT_GRAY;
	bPlus.activeLine=2;
	bPlus.font=&font;
	bPlus.fontColor=INT_BLACK;
	bPlus.margin.set(10,1);
	bPlus.bgColor=INT_YELLOW;
	bPlus.stick(DIR_RIGHT,5,0);

	bMin.copy(bPlus);
	bMin.text=_T("-");
	bMin.stick(DIR_RIGHT,3,0);

	bAuto.copy(bPlus);
	bAuto.text=_T("*");
	bAuto.stick(DIR_RIGHT,3,0);

	tbNoise.copy(tbCoefs);
	tbNoise.size.x=60;
	tbNoise.stick(DIR_DOWN,0,5,&tbCoefs);

	bNoise.copy(bPlus);
	bNoise.text=_T("Noise");
	bNoise.stick(DIR_RIGHT,5,0);

	tbStart.copy(tbNoise);
	tbStart.size.x=70;
	tbStart.stick(DIR_DOWN,0,5,&tbNoise);

	tbEnd.copy(tbStart);
	tbEnd.stick(DIR_RIGHT,5,0,&tbStart);

	bInter.copy(bPlus);
	bInter.text=_T("Set");
	bInter.stick(DIR_RIGHT,5,0,&tbEnd);

	tbSamples.copy(tbStart);
	tbSamples.size.x=40;
	tbSamples.stick(DIR_DOWN,0,5,&tbStart);

	tbPoints.copy(tbSamples);
	tbPoints.stick(DIR_RIGHT,5,0,&tbSamples);

	bSamples.copy(bPlus);
	bSamples.text=_T("Samples");
	bSamples.stick(DIR_RIGHT,5,0,&tbPoints);

	bCapture.copy(bSamples);
	bCapture.text=_T("Capture");
	bCapture.stick(DIR_DOWN,0,5,&tbSamples);

	bImport.copy(bSamples);
	bImport.text=_T("Import");
	bImport.stick(DIR_RIGHT,5,0,&bCapture);

	bLog.copy(bSamples);
	bLog.text=_T("Log");
	bLog.checkable=TRUE;
	bLog.stick(DIR_RIGHT,5,0,&bImport);

	tbFile.copy(tbSamples);
	tbFile.size.x=200;
	tbFile.text=_T("FileName.dat");
	tbFile.stick(DIR_DOWN,0,5,&bCapture);
	
	if(!setSamples(DEF_SAMPLES,DEF_POINTS))
		return FALSE;
	setInterval(DEF_START,DEF_END);
	setNoise(0);
	interpolate();

	add(&fg);
	add(&tbCoefs);
	add(&bPlus);
	add(&bMin);
	add(&bAuto);
	add(&tbNoise);
	add(&bNoise);
	add(&tbStart);
	add(&tbEnd);
	add(&bInter);
	add(&tbSamples);
	add(&tbPoints);
	add(&bSamples);
	add(&bCapture);
	add(&bImport);
	add(&bLog);
	add(&tbFile);

	return TRUE;
}
void LSM::draw2D()
{
	font.color=INT_GREEN;
	font.x=160;
	font.y=40;
	glPrint(&font,formula);

	font.color=INT_WHITE;
	font.x=10;
	glPrint(&font,_T("LSM %2d coefs:"),coefCount);

	font.color=INT_WGRAY;
	font.y=250;
	glPrint(&font,_T("error: %+le"),error);

	font.y+=10;
	font.color=INT_YELLOW;
	for(int i=0;i<coefCount;i++)
	{
		font.y+=18;
		glPrint(&font,_T("coef%d: %+lf"),i,coefs[i]);
	}
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	return lsm.main(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
}